﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
#if !SILVERLIGHT
using System.ComponentModel.Design.Serialization;
#endif
using System.Globalization;
using UIT.Style;
using System.Reflection;
using UIT.Utils;

namespace UIT.Converters
{
    /// <summary>Converts instances of other types to and from instances of <see cref="T:System.Windows.Thickness" />.</summary>
    public class ThicknessConverter : TypeConverter
    {
        /// <summary>Determines whether the type converter can create an instance of <see cref="T:System.Windows.Thickness" /> from a specified type.</summary>
        /// <returns>true if the type converter can create an instance of <see cref="T:System.Windows.Thickness" /> from the specified type; otherwise, false.</returns>
        /// <param name="typeDescriptorContext">The context information of a type.</param>
        /// <param name="sourceType">The source type that the type converter is evaluating for conversion.</param>
        public override bool CanConvertFrom(ITypeDescriptorContext typeDescriptorContext, Type sourceType)
        {
            switch (Type.GetTypeCode(sourceType))
            {
                case TypeCode.Int16:
                case TypeCode.UInt16:
                case TypeCode.Int32:
                case TypeCode.UInt32:
                case TypeCode.Int64:
                case TypeCode.UInt64:
                case TypeCode.Single:
                case TypeCode.Double:
                case TypeCode.Decimal:
                case TypeCode.String:
                    {
                        return true;
                    }
            }
            return false;
        }
        /// <summary>Determines whether the type converter can convert an instance of <see cref="T:System.Windows.Thickness" /> to a different type. </summary>
        /// <returns>true if the type converter can convert this instance of <see cref="T:System.Windows.Thickness" /> to the <paramref name="destinationType" />; otherwise, false.</returns>
        /// <param name="typeDescriptorContext">The context information of a type.</param>
        /// <param name="destinationType">The type for which the type converter is evaluating this instance of <see cref="T:System.Windows.Thickness" /> for conversion.</param>
        public override bool CanConvertTo(ITypeDescriptorContext typeDescriptorContext, Type destinationType)
        {
            return
#if !SILVERLIGHT
                destinationType == typeof(InstanceDescriptor) || 
#endif
 destinationType == typeof(string);
        }
        /// <summary>Attempts to create an instance of <see cref="T:System.Windows.Thickness" /> from a specified object. </summary>
        /// <returns>An instance of <see cref="T:System.Windows.Thickness" /> created from the converted <paramref name="source" />.</returns>
        /// <param name="typeDescriptorContext">The context information for a type.</param>
        /// <param name="cultureInfo">The <see cref="T:System.Globalization.CultureInfo" /> of the type being converted.</param>
        /// <param name="source">The <paramref name="source" /><see cref="T:System.Object" /> being converted.</param>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="source" /> object is a null reference (Nothing in Visual Basic).</exception>
        /// <exception cref="T:System.ArgumentException">The example object is not a null reference and is not a valid type that can be converted to a <see cref="T:System.Windows.Thickness" />.</exception>
        public override object ConvertFrom(ITypeDescriptorContext typeDescriptorContext, CultureInfo cultureInfo, object source)
        {
#if !SILVERLIGHT
            if (source == null)
            {
                throw base.GetConvertFromException(source);
            }
#endif
            if (source is string)
            {
                return ThicknessConverter.FromString((string)source, cultureInfo);
            }
            if (source is Unit)
            {
                return new Thickness((Unit)source);
            }

            if (source is double)
            {
                return new Thickness((double)source);
            }
            return new Thickness(Convert.ToDouble(source, cultureInfo));
        }
        /// <summary>Attempts to convert an instance of <see cref="T:System.Windows.Thickness" /> to a specified type. </summary>
        /// <returns>The type that is created when the type converter converts an instance of <see cref="T:System.Windows.Thickness" />.</returns>
        /// <param name="typeDescriptorContext">The context information of a type.</param>
        /// <param name="cultureInfo">The <see cref="T:System.Globalization.CultureInfo" /> of the type being converted.</param>
        /// <param name="value">The instance of <see cref="T:System.Windows.Thickness" /> to convert.</param>
        /// <param name="destinationType">The type that this instance of <see cref="T:System.Windows.Thickness" /> is converted to.</param>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="value" /> object is not a null reference (Nothing) and is not a Brush, or the <paramref name="destinationType" /> is not one of the valid types for conversion.</exception>
        /// <exception cref="T:System.ArgumentException">The <paramref name="value" /> object is a null reference.</exception>
        public override object ConvertTo(ITypeDescriptorContext typeDescriptorContext, CultureInfo cultureInfo, object value, Type destinationType)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value");
            }
            if (null == destinationType)
            {
                throw new ArgumentNullException("destinationType");
            }
            if (!(value is Thickness))
            {
                throw new ArgumentException(string.Format("UnexpectedParameterType {0}. Parameter type expected {1}", new object[]
                {
                    value.GetType(), 
                    typeof(Thickness)
                }), "value");
            }
            Thickness th = (Thickness)value;
            if (destinationType == typeof(string))
            {
                return ThicknessConverter.ToString(th, cultureInfo);
            }
#if !SILVERLIGHT
            if (destinationType == typeof(InstanceDescriptor))
            {
                ConstructorInfo constructor = typeof(Thickness).GetConstructor(new Type[]
                {
                    typeof(double), 
                    typeof(double), 
                    typeof(double), 
                    typeof(double)
                });
                return new InstanceDescriptor(constructor, new object[]
                {
                    th.Left, 
                    th.Top, 
                    th.Right, 
                    th.Bottom
                });
            }
#endif
            throw new ArgumentException(string.Format("CannotConvertType {0} to {1}", new object[]
            {
                typeof(Thickness), 
                destinationType.FullName
            }));
        }

        internal static string ToString(double l, CultureInfo cultureInfo)
        {
            if (DoubleUtil.IsNaN(l))
            {
                return "Auto";
            }
            return Convert.ToString(l, cultureInfo);
        }

        internal static string ToString(Thickness th, CultureInfo cultureInfo)
        {
            if (th.IsZero)
                return null;

            char numericListSeparator = GetSeparator(cultureInfo);
            StringBuilder stringBuilder = new StringBuilder(64);

            stringBuilder.Append(th.Left);
            stringBuilder.Append(numericListSeparator);
            stringBuilder.Append(th.Top);
            stringBuilder.Append(numericListSeparator);
            stringBuilder.Append(th.Right);
            stringBuilder.Append(numericListSeparator);
            stringBuilder.Append(th.Bottom);

            return stringBuilder.ToString();
        }

        private static char GetSeparator(CultureInfo cultureInfo)
        {
            char numericListSeparator = ',';
            NumberFormatInfo instance = NumberFormatInfo.GetInstance(cultureInfo);
            if (instance.NumberDecimalSeparator.Length > 0 && numericListSeparator == instance.NumberDecimalSeparator[0])
            {
                numericListSeparator = ';';
            }
            return numericListSeparator;
        }
        internal static Thickness FromString(string s, CultureInfo cultureInfo)
        {
            string[] values = s.Split(GetSeparator(cultureInfo), ' ');

            Unit[] array = new Unit[4];
            int i = 0;
            for (; i < values.Length; i++)
            {
                if (i >= 4)
                {
                    i = 5;
                    break;
                }
                array[i] = UnitConverter.FromString(values[i], cultureInfo);
            }
            switch (i)
            {
                case 1:
                    {
                        return new Thickness(array[0]);
                    }
                case 2:
                    {
                        return new Thickness(array[0], array[1], array[0], array[1]);
                    }
                case 4:
                    {
                        return new Thickness(array[0], array[1], array[2], array[3]);
                    }
            }
            throw new FormatException(string.Format("InvalidStringThickness {0}", new object[]
            {
                s
            }));
        }
        /// <summary>Initializes a new instance of the <see cref="T:System.Windows.ThicknessConverter" /> class. </summary>
        public ThicknessConverter()
        {
        }
    }
}
